package cn.maihe.elg.operation.centers.hymake.handler;

import cn.maihe.elg.operation.centers.hymake.auth.HyMakeCenterAuthConfig;
import cn.maihe.elg.operation.centers.hymake.auth.HyMakeHeaderInfo;
import cn.maihe.elg.operation.centers.hymake.dto.HyMakeBaseRespDTO;
import cn.maihe.elg.operation.centers.hymake.enums.HyMakeRespCodeEnum;
import cn.maihe.elg.operation.centers.hymake.exception.HyMakeException;
import cn.maihe.elg.operation.centers.supports.annotation.CenterTradeService;
import cn.maihe.elg.operation.exception.CenterException;
import cn.maihe.elg.operation.exception.ExceptionUtils;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.model.enums.CenterProviderEnum;
import cn.maihe.elg.operation.utils.DateUtil;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.IdGenerate;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.SocketTimeoutException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * @Description
 * @Author caixh
 * @Date 2022/3/30.
 */
@RestController
@Slf4j
public class HyMakeCenterHandler {
    public static final CenterProviderEnum centerProvider = CenterProviderEnum.HYMAKE;

    /**
     * 中心服务商的接口实现类
     */
    private final Map<String, HyMakeCenterTradeService> tradeServiceMap = new HashMap<>();
    /**
     * 能够使用与该中心服务商的 中心信息
     */
    public static final List<String> enableCenterNos = Lists.newArrayList(
            CenterNoEnum.WUYISHAN.getCenterNo(),CenterNoEnum.WUYISHAN.getCenterNo());

    public HyMakeCenterHandler(@SuppressWarnings("rawtypes") final Collection<HyMakeCenterTradeService> baseCenterServices) {
        super();
        baseCenterServices.forEach(item -> {
            CenterTradeService ts = AnnotationUtils.findAnnotation(item.getClass(), CenterTradeService.class);
            if (centerProvider == ts.provider()) {
                tradeServiceMap.put(ts.tradeType().name().toLowerCase(), item);
            }
        });
    }


    /**
     * 武夷山中心 http://ip:port/xxx/hb/{tradeType}
     */
    @Profile(value = {"dev", "test"})
    @PostMapping(value = "/test/v1/center/{centerNo}/{orgCode}/bh/{tradeType}", consumes = {"application/json", "application/xml"})
    public @ResponseBody
   Object testMockService(@PathVariable("centerNo") String centerNo, @PathVariable("orgCode") String orgCode, @PathVariable("tradeType") final String tradeType, @RequestHeader(required = false) Map<String, Object> headerMap, @RequestBody final String clearBizReqJsonBody, final HttpServletRequest req, final HttpServletResponse resp) {
        DeferredResult<Object> dr = new DeferredResult<Object>();
        if (!this.tradeServiceMap.containsKey(tradeType.toLowerCase())) {
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "不支持的交易类型"));
            return dr;
        }
        CenterNoEnum centerNoEnum;
        try {
            centerNoEnum = CenterNoEnum.resolve(centerNo);
        } catch (Exception e) {
            log.error("暂不支持的交易中心[{}]:{}", centerNo, e.getMessage(), e);
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "暂不支持的交易中心[" + centerNo + "]"));
            return dr;
        }
        HyMakeCenterTradeService hyMakeCenterTradeService = this.tradeServiceMap.get(tradeType.toLowerCase());
        HyMakeCenterAuthConfig centerAuthConfig = hyMakeCenterTradeService.getAuthConfig(centerNoEnum);

        HyMakeHeaderInfo headerInfo;
        // 加密业务数据
//        String encryptAndB64ReqBody = centerAuthConfig.getHyMakeEncryption().encrypt(clearBizReqJsonBody);
        String encryptAndB64ReqBody = clearBizReqJsonBody;
        try {
            headerInfo = new HyMakeHeaderInfo();
            headerInfo.setVersion(centerAuthConfig.getCenterInfo().getVersion());
            headerInfo.setAppid(centerAuthConfig.getCenterInfo().getVersion());
            headerInfo.setRequestid(IdGenerate.getIdStr());
            headerInfo.setTimestamp(String.valueOf(System.currentTimeMillis()));
            //明文业务数据签名
            headerInfo.setSign(centerAuthConfig.getHyMakeAuthorization().signature(headerInfo.getRequestid(), clearBizReqJsonBody, headerInfo.getTimestamp()));
        } catch (Exception e) {
            log.error("请求头参数错误", e.getMessage(), e);
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "请求头参数错误[" + e.getMessage() + "]"));
            return dr;
        }

        Map<String, Object> mockHeadMap = BeanMap.create(headerInfo);

            DeferredResult deferredResult = this.doService(centerNo, orgCode, tradeType, mockHeadMap, req, resp, encryptAndB64ReqBody);
        deferredResult.setResultHandler(r->{
            //解密还原不重新计算签名
            Object respData = deferredResult.getResult();
            if (respData != null && respData instanceof String) {
                HyMakeBaseRespDTO respDTO = JSON.parseObject((String) respData, HyMakeBaseRespDTO.class);
                Object bizRespData = respDTO.getData();
                if (bizRespData instanceof String && bizRespData != null) {
//                    String clearBizData = centerAuthConfig.getHyMakeEncryption().decrypt((String) bizRespData);
                    String clearBizData = (String)bizRespData;
                    JSONObject jsonObject = JSON.parseObject(clearBizData);
                    respDTO.setData(jsonObject);
                }
                dr.setResult(respDTO);
                log.info("测试还原数据结果：{}",JSON.toJSONString(respDTO));
            }
        });

        return dr;
    }

    /**
     * 武夷山中心 http://ip:port/xxx/hb/{tradeType}
     */
    @PostMapping(value = "/v1/center/{centerNo}/{orgCode}/bh/{tradeType}", consumes = {"application/json", "application/xml"})
    public @ResponseBody
    DeferredResult<Object> service(@PathVariable("centerNo") String centerNo, @PathVariable("orgCode") String orgCode, @PathVariable("tradeType") final String tradeType, @RequestHeader(required = false) Map<String, Object> headerMap, @RequestBody final String encryptReqBody, final HttpServletRequest req, final HttpServletResponse resp) {
        return this.doService(centerNo, orgCode, tradeType, headerMap, req, resp, encryptReqBody);
    }


    @SuppressWarnings("unchecked")
    protected DeferredResult<Object> doService(String centerNo, String orgCode, String tradeType, Map<String, Object> headerMap, final HttpServletRequest req, final HttpServletResponse resp, final String encryptReqBody) {

        DeferredResult<Object> dr = new DeferredResult<Object>();
        if (!this.tradeServiceMap.containsKey(tradeType.toLowerCase())) {
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "不支持的交易类型"));
            return dr;
        }
        CenterNoEnum centerNoEnum;
        try {
            centerNoEnum = CenterNoEnum.resolve(centerNo);
        } catch (Exception e) {
            log.error("暂不支持的交易中心[{}]:{}", centerNo, e.getMessage(), e);
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "暂不支持的交易中心[" + centerNo + "]"));
            return dr;
        }
        log.info("请求头部参数:{}", JSON.toJSONString(headerMap));
        log.info("请求报文参数[明文加密字符串]:{}", JSON.toJSONString(encryptReqBody));

        HyMakeHeaderInfo headerInfo;
        try {
            headerInfo = HyMakeHeaderInfo.of(headerMap);
        } catch (Exception e) {
            log.error("请求头参数错误", e.getMessage(), e);
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "请求头参数错误[" + e.getMessage() + "]"));
            return dr;
        }

        HyMakeCenterTradeService hyMakeCenterTradeService = this.tradeServiceMap.get(tradeType.toLowerCase());
        HyMakeCenterAuthConfig hyMakeCenterAuthConfig = hyMakeCenterTradeService.getAuthConfig(centerNoEnum);

        CompletableFuture
                .supplyAsync(() -> {
                    try {
                        //解密&验签后获取bizReqBody
                        Map<String, Object> clearParams = this.decryptAndVerifySignature(headerInfo, encryptReqBody, hyMakeCenterAuthConfig);
                        return hyMakeCenterTradeService.doService(centerNoEnum, orgCode, clearParams, null);
                    } catch (Exception e) {
                        throw buildHyMakeException(e);
                    }
                })
                .whenCompleteAsync((baseResp, t) -> {
                    if (t != null) {
                        HyMakeException te = buildHyMakeException(t);
                        log.error("中心：{},交易类型：{},服务错误：{}", centerNoEnum.getCenterName(), tradeType, t.getMessage(), t);
                        baseResp = buildRespException(te.getStatusEnum(), te.getMessage());
                    }

                    //加密加签操作
                    String encryptAllResp = this.encryptAndSignature(headerInfo, resp, baseResp, hyMakeCenterAuthConfig, centerNoEnum, tradeType);

                    dr.setResult(encryptAllResp);
                    log.info("中心：{},交易类型：{},响应报文：{}", centerNoEnum.getCenterName(), tradeType, dr.getResult());
                });
        return dr;
    }

    private String encryptAndSignature(HyMakeHeaderInfo reqHeader, HttpServletResponse response, HyMakeBaseRespDTO baseResp, HyMakeCenterAuthConfig hyMakeCenterAuthConfig, CenterNoEnum centerNoEnum, String tradeType) {
        log.info("中心：{},交易类型：{},响应报文明文：{}", centerNoEnum.getCenterName(), tradeType, JSON.toJSONString(baseResp));
        Object data = baseResp.getData();
        //响应业务数据加密
//        if (data != null) {
//            String clearBizRespData = JSON.toJSONString(data);
//            String encryptBizRespData = hyMakeCenterAuthConfig.getHyMakeEncryption().encrypt(clearBizRespData);
////            String encryptBizRespData = clearBizRespData;
//            baseResp.setData(encryptBizRespData);
//        }

        //整个报文签名
        HyMakeHeaderInfo respHead = new HyMakeHeaderInfo();
        respHead.setVersion(hyMakeCenterAuthConfig.getCenterInfo().getVersion());
        respHead.setAppid(hyMakeCenterAuthConfig.getCenterInfo().getAppId());
//        respHead.setSign();
        respHead.setRequestid(reqHeader.getRequestid());
        respHead.setTimestamp(DateUtil.getFulltime());

        String allRespJson = JSON.toJSONString(baseResp);
        String signature = hyMakeCenterAuthConfig.getHyMakeAuthorization().signature(respHead.getRequestid(), allRespJson, respHead.getTimestamp());
        respHead.setSign(signature);

        BeanMap.create(respHead).forEach((k, v) -> response.setHeader((String) k, (String) v));

        String clearAllRespJson = JSON.toJSONString(baseResp);
        log.info("中心：{},交易类型：{},响应报文头：{},响应报文密文：{}", centerNoEnum.getCenterName(), tradeType, JSON.toJSONString(respHead), clearAllRespJson);

//        String encryptAllResp = hyMakeCenterAuthConfig.getHyMakeEncryption().encrypt(clearAllRespJson);

        return clearAllRespJson;
    }

    /**
     * 解密验签及转换param
     *
     * @param headerInfo
     * @param encryptAndB64ReqBody
     * @param hyMakeCenterAuthConfig
     * @return
     */
    private Map<String, Object> decryptAndVerifySignature(HyMakeHeaderInfo headerInfo, String encryptAndB64ReqBody, HyMakeCenterAuthConfig hyMakeCenterAuthConfig) {
        /**
         * 业务数据明文加密后字符串
         */
        Assert.hasText(encryptAndB64ReqBody, "请求报文为空");


        // 解密、验签及反序列化
        String clearBizReqJsonBody;
        try {
            //clearBizReqJsonBody = hyMakeCenterAuthConfig.getHyMakeEncryption().decrypt(encryptAndB64ReqBody);
            //body部分明文传输
            clearBizReqJsonBody = encryptAndB64ReqBody;
            ElgLogger.info(log, log -> log.info("解密完请求报文:{}", clearBizReqJsonBody));
        } catch (Exception e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "报文解密失败:" + e.getMessage(), e);
        }

        try {
            // 验签
            boolean verifyTF = this.verifySignature(hyMakeCenterAuthConfig, clearBizReqJsonBody, headerInfo);
            if (!verifyTF) {
                throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "验签失败");
            }
        } catch (Exception e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "验签异常:" + e.getMessage(), e);
        }

        Map<String, Object> params = JSON.parseObject(clearBizReqJsonBody, Map.class);

        return params;
    }

    public HyMakeException buildHyMakeException(CenterException ce) {
        HyMakeRespCodeEnum respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        //待优化明细
        if (ce.getInnerRespCode().isFailure()) {
            respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        } else if (ce.getInnerRespCode().isException()) {
            respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        } else if (ce.getInnerRespCode().isTimeOut()) {
            respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        }
        return HyMakeException.of(respCodeEnum, ce.getMessage());
    }


    protected boolean verifySignature(HyMakeCenterAuthConfig authConfig, String clearBizReqJsonBody, HyMakeHeaderInfo headerInfo) {
        try {
            return authConfig.getHyMakeAuthorization().verifySignature(headerInfo.getRequestid(), clearBizReqJsonBody, headerInfo.getSign(), headerInfo.getTimestamp());
        } catch (Exception e) {
            throw HyMakeException.error("验签失败:" + e.getMessage(), e);
        }
    }

    private HyMakeBaseRespDTO buildRespException(HyMakeRespCodeEnum statusEnum, String msg) {
        return HyMakeBaseRespDTO.of(statusEnum, msg);
    }

    private HyMakeException buildHyMakeException(Throwable t) {
        HyMakeException te = ExceptionUtils.isAssignableFrom(HyMakeException.class, t);
        if (te == null) {
            CenterException ce = ExceptionUtils.isAssignableFrom(CenterException.class, t);
            if (ce != null) {
                te = buildHyMakeException(ce);
            } else {
                SocketTimeoutException timeout = ExceptionUtils.isAssignableFrom(SocketTimeoutException.class, t);
                if (timeout != null) {
                    te = HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "请求超时", t);
                } else {
                    te = HyMakeException.error("请求错误:" + t.getMessage(), t);
                }
            }
        }
        return te;
    }

}
